package com.intretech.umsin.datasource.codemapping.service.impl;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.intretech.umsin.common.entity.PageResult;
import com.intretech.umsin.common.utils.ReflectionUtils;
import com.intretech.umsin.common.web.ApiResult;
import com.intretech.umsin.datasource.codemapping.annotations.CodeMappingConfig;
import com.intretech.umsin.datasource.codemapping.entity.Assignment;
import com.intretech.umsin.datasource.codemapping.entity.CodeNameMap;
import com.intretech.umsin.datasource.codemapping.entity.MappingConfig;
import com.intretech.umsin.datasource.codemapping.entity.RootClassInfo;
import com.intretech.umsin.datasource.codemapping.service.CodeMappingDBService;
import com.intretech.umsin.datasource.codemapping.service.CodeMappingService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @version V1.0
 * @ClassName: CodeMappingProcessorServiceImpl
 * @Description:
 * @author: 李启联
 * @date: 2020-03-16 14:06
 * @Copyright: Copyright (c), 2020
 * 申明：本内容未经授权,禁止外泄以及用于其他的商业项目.
 */
@Service
@Slf4j
public class CodeMappingServiceImpl implements CodeMappingService {
    
    @Autowired
    CodeMappingDBService codeMappingDBService;

    /**
     * 得到目标映射对象列表
     * @param targetClassKeyProperty 名"在需要查找目标对象所对应的键(Key)属性名
     * @param targetClassFieldProperty "名"在需要查找目标对象所对应的字段属性名
     * @param targetClass "名"需要查找目标对象类型
     * @param targetClassName "名"需要查找目标对象全类名
     * @param fieldCodePropertyVal "码"在类中对应的字段对应的值,为减轻数据库压力(在方法返回列表对象时，一次性返回)
     * @return
     */
    @Override
    public List<CodeNameMap> getCodeNameMaps(String targetClassKeyProperty, String targetClassFieldProperty, Class targetClass, String targetClassName, Set<Object> fieldCodePropertyVal) {
        List<CodeNameMap> codeNameMaps = new ArrayList();
        if (fieldCodePropertyVal.size() > 0) {
            fieldCodePropertyVal.forEach(v -> {
                CodeNameMap codeNameMap = getCodeNameMap(targetClassKeyProperty, targetClassFieldProperty, targetClass, targetClassName, v);
                if (codeNameMap != null) {
                    codeNameMaps.add(codeNameMap);
                }
            });
        }
        return codeNameMaps;
    }

    /**
     * 得到目标映射对象 Code-Name 码名信息
     *
     * @param targetClassKeyProperty   名"在需要查找目标对象所对应的键(Key)属性名
     * @param targetClassFieldProperty "名"在需要查找目标对象所对应的字段属性名
     * @param targetClass              "名"需要查找目标对象类型
     * @param targetClassName          "名"需要查找目标对象全类名
     * @param fieldCodePropertyVal     "码"在类中对应的字段对应的值,为减轻数据库压力(在方法返回列表对象时，一次性返回)
     * @return
     */
    @Override
    public CodeNameMap getCodeNameMap(String targetClassKeyProperty, String targetClassFieldProperty, Class targetClass, String targetClassName, Object fieldCodePropertyVal) {

        //return codeMappingDBService.getCodeNameMapFromDB(targetClassKeyProperty,targetClassFieldProperty,targetClass,targetClassName,fieldCodePropertyVal) ;
        if (StringUtils.isNotBlank(targetClassName)) {
            try {
                targetClass = Class.forName(targetClassName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                log.warn("实体类" + targetClassName + "不存在" + e.getMessage());
                return null;
            }
        }
        Object targetObj = codeMappingDBService.getTargetObjectV2(targetClass, targetClassKeyProperty, fieldCodePropertyVal.toString()); //codeMappingDBService.getTargetObject(targetClass,fieldCodePropertyVal.toString());
        Object code = null, name = null;
        if (null != targetObj) {
            if(targetClassKeyProperty.contains(".")){
                code = ReflectionUtils.getTargetValue(targetObj, targetClassKeyProperty);
            }else {
                code = ReflectionUtils.getFieldValue(targetObj, targetClassKeyProperty);
            }
            name = ReflectionUtils.getFieldValue(targetObj, targetClassFieldProperty);
            CodeNameMap codeNameMap = new CodeNameMap(code, name);
            return codeNameMap;
        }
        return null;
    }


    public static void main(String[] args) {
        System.out.println(CodeNameMap.class.getClass().getTypeName());
    }

    /**
     * 对需要处理对象作[码-名]映射
     * @param result 需要处理对象
     * @param mappingConfigs 对应的映射配置
     * @param rootClassInfo 待映射对象实体封装类
     * @return
     */
    @Override
    public Object processDoMapping(Object result, List<MappingConfig> mappingConfigs, RootClassInfo rootClassInfo) {
        List<MappingConfig> configs = null;

        if (StringUtils.isBlank(rootClassInfo.getRootClassName()) && rootClassInfo.getRootClass() == Object.class) {
            log.info("@DoMapping方法注解中,码名映射实体对象类型或类全名必须设置其中一个属性[rootClass,rootClassName]");
            return result;
        }
        Class rootClass = null;
        if (rootClassInfo.getRootClass() == Object.class) {
            try {
                rootClass = Class.forName(rootClassInfo.getRootClassName());
            } catch (ClassNotFoundException e) {
                log.error(e.getMessage());
            }
        } else {
            rootClass = rootClassInfo.getRootClass();
        }

        if (!CollectionUtils.isEmpty(mappingConfigs)) {
            configs = mappingConfigs;
        } else {
            configs = getMappingConfigs(rootClass);
        }

        //处理前校验设置参数
        String checkParamResult = checkMappingParams(rootClassInfo, mappingConfigs);
        if (checkParamResult.length() > 0) {
            log.info(checkParamResult);
            return result;
        }

        Object obj = result;
        if (result instanceof ApiResult) {//支持Controller层返回CommonResponse数据类型的支持
            obj = ((ApiResult) result).getData();
        }

        Map<String, List<CodeNameMap>> codeNamesMap = storeFieldCodePropertyValues(obj, configs);

        fillResult(obj, configs, codeNamesMap);
        return result;
    }


    //填充结果集主方法
    private void fillResult(Object result, List<MappingConfig> configs, Map<String, List<CodeNameMap>> codeNamesMap) {
        if (result instanceof Collection) {
            //如果是针对的是列表并且其内容是"Assignment"实例的实体对象
            if (((Collection) result).stream().allMatch(r -> r instanceof Assignment)) {
                List<MappingConfig> finalConfigs = configs;
                ((Collection) result).stream().forEach(o -> {
                    fillObjFieldValue(o, finalConfigs, codeNamesMap);
                });
            } else if (((Collection) result).stream().allMatch(r -> r instanceof Map)) {//如果是针对的是列表并且其内容是Map对象
                List<MappingConfig> finalConfigs = configs;
                ((Collection) result).stream().forEach(m -> {
                    fillMapFieldValue((Map) m, finalConfigs, codeNamesMap);
                });
            }
        }
        else if (result instanceof PageResult) {
            List ls = ((PageResult) result).getRecords();
            if (ls.stream().allMatch(r -> r instanceof Assignment)) {//如果是针对的是Page 并且其列表内容是"Assignment"实例的实体对象
                List<MappingConfig> finalConfigs = configs;
                ls.stream().forEach(o -> {
                    fillObjFieldValue(o, finalConfigs, codeNamesMap);
                });
            } else if (ls.stream().allMatch(r -> r instanceof Map)) {//如果是针对的是Page 并且其列表内容是Map实例的实体对象
                List<MappingConfig> finalConfigs = configs;
                ((Collection) result).stream().forEach(m -> {
                    fillMapFieldValue((Map) m, finalConfigs, codeNamesMap);
                });
            }
        }
        else if(result instanceof IPage){
            List ls = ((IPage) result).getRecords();
            if (ls.stream().allMatch(r -> r instanceof Assignment)) {//如果是针对的是Page 并且其列表内容是"Assignment"实例的实体对象
                List<MappingConfig> finalConfigs = configs;
                ls.stream().forEach(o -> {
                    fillObjFieldValue(o, finalConfigs, codeNamesMap);
                });
            } else if (ls.stream().allMatch(r -> r instanceof Map)) {//如果是针对的是Page 并且其列表内容是Map实例的实体对象
                List<MappingConfig> finalConfigs = configs;
                ((Collection) result).stream().forEach(m -> {
                    fillMapFieldValue((Map) m, finalConfigs, codeNamesMap);
                });
            }
        }

        else if (result instanceof Assignment) {//如果该对象是"Assignment"实例对象
            fillObjFieldValue(result, configs, codeNamesMap);
        } else if (result instanceof Map) {//如果该对象是Map实例对象
            fillMapFieldValue((Map) result, configs, codeNamesMap);
        }
    }

    /**
     * 向实体对象对应字段填充映射值
     * @param obj 需要填充的实体对象
     * @param configs 映射配置列表
     * @param codeNamesMap 找出的码表
     * @return
     */
    private Object fillObjFieldValue(Object obj, List<MappingConfig> configs, Map<String, List<CodeNameMap>> codeNamesMap) {

        configs.stream().forEach(c -> {
            List<CodeNameMap> codeNameMaps = codeNamesMap.get(c.getFieldNameProperty());
            Object o = ReflectionUtils.getFieldValue(obj, c.getFieldCodeProperty());
            String fieldCodeVal = (o == null ? "" : o.toString());
            if (!CollectionUtils.isEmpty(codeNameMaps)) {
                Optional<CodeNameMap> cn = codeNameMaps.stream().filter(n -> n.getCode().toString().equals(fieldCodeVal)).findAny();
                if (cn.isPresent()) {
                    ReflectionUtils.setFieldValue(obj, c.getFieldNameProperty(), cn.get().getName());
                }
            }
        });
        return obj;
    }

    /**
     * 向Map对象填充映射值
     * @param map
     * @param configs
     * @param codeNamesMap
     * @return
     */
    private Map fillMapFieldValue(Map map, List<MappingConfig> configs, Map<String, List<CodeNameMap>> codeNamesMap) {

        configs.stream().forEach(c -> {
            List<CodeNameMap> codeNameMaps = codeNamesMap.get(c.getFieldNameProperty());
            if (map.containsKey(c.getFieldCodeProperty())) {
                String fieldCodeVal = map.get(c.getFieldCodeProperty()).toString();
                Optional<CodeNameMap> cn = codeNameMaps.stream().filter(n -> n.getCode().toString().equals(fieldCodeVal)).findAny();
                if (cn.isPresent()) {
                    map.put(c.getFieldNameProperty(), cn.get().getName());
                }
            }
        });
        return map;
    }


    /**
     * 先把所有需要映射的目标对象先查询并存储下来
     * @param result
     * @param mappingConfigs
     * @return
     */
    private Map<String, List<CodeNameMap>> storeFieldCodePropertyValues(Object result, List<MappingConfig> mappingConfigs) {
        Map<String, List<CodeNameMap>> codeNames = new TreeMap<>();

        mappingConfigs.stream().forEach(c -> {
            List<Object> codeVals = new ArrayList<>();
            if (result instanceof Collection) {
                if (((Collection) result).stream().allMatch(r -> r instanceof Assignment)) {
                    codeVals = ReflectionUtils.fetchElementPropertyToList((Collection) result, c.getFieldCodeProperty());
                } else if (((Collection) result).stream().allMatch(r -> r instanceof Map)) {
                    List<Object> ls = (List<Object>) ((Collection) result).stream().filter(m -> ((Map) m).containsKey(c.getFieldCodeProperty())).map(m -> ((Map) m).get(c.getFieldCodeProperty())).collect(Collectors.toList());
                    codeVals.addAll(ls);
                }
            }

            else if (result instanceof PageResult) {
                List ls = ((PageResult) result).getRecords();
                if (ls != null && ls.size() > 0) {
                    if (ls.get(0) != null && ls.get(0) instanceof Assignment) {
                        codeVals = ReflectionUtils.fetchElementPropertyToList(ls, c.getFieldCodeProperty());
                    }
                }
            }
            else if (result instanceof IPage) {
                List ls = ((IPage) result).getRecords();
                if (ls != null && ls.size() > 0) {
                    if (ls.get(0) != null && ls.get(0) instanceof Assignment) {
                        codeVals = ReflectionUtils.fetchElementPropertyToList(ls, c.getFieldCodeProperty());
                    }
                }
            }
            else if (result instanceof Assignment) {
                codeVals.add(ReflectionUtils.getFieldValue(result, c.getFieldCodeProperty()));
            }
            else if (result instanceof Map) {
                if (((Map) result).containsKey(c.getFieldCodeProperty())) {
                    Object mpVal = ((Map) result).get(c.getFieldCodeProperty());
                    codeVals.add(mpVal);
                }
            }
            List<Object> codeValsNullable = codeVals.stream().filter(Objects::nonNull).collect(Collectors.toList());//过滤掉空值
            List<CodeNameMap> codeNameMap = getCodeNameMaps(c.getTargetClassKeyProperty(), c.getTargetClassFieldProperty(), c.getTargetClass(), c.getTargetClassName(), new TreeSet(codeValsNullable));
            codeNames.put(c.getFieldNameProperty(), codeNameMap);
        });

        //该逻辑是处理doMapping的字段的参引字段也是由doMapping而来
        codeNames.forEach((k, v) -> {
            if (CollectionUtils.isEmpty(v)) {
                Optional<MappingConfig> optConf = mappingConfigs.stream().filter(m -> m.getFieldNameProperty().equals(k)).findAny();
                if (optConf.isPresent()) {
                    MappingConfig conf = optConf.get();
                    String fieldCodeProperty = conf.getFieldCodeProperty();
                    if (codeNames.containsKey(fieldCodeProperty)) {
                        List<CodeNameMap> codeNameMaps = codeNames.get(fieldCodeProperty);
                        if (!CollectionUtils.isEmpty(codeNameMaps)) {
                            Set<Object> codes = codeNameMaps.stream().filter(Objects::nonNull).map(CodeNameMap::getName).filter(Objects::nonNull).collect(Collectors.toSet());
                            List<CodeNameMap> subCodeNameMap = getCodeNameMaps(conf.getTargetClassKeyProperty(), conf.getTargetClassFieldProperty(), conf.getTargetClass(), conf.getTargetClassName(), new TreeSet<>(codes));
                            codeNames.put(k, subCodeNameMap);
                        }
                    }
                }
            }
        });

        return codeNames;
    }

    /**
     * 遍历得到实体对象下属性标注的[码-名]映射配置
     * @param result 映射配置列表
     * @return
     */
    @Deprecated
    private List<MappingConfig> getMappingConfigs(Object result) {
        List<MappingConfig> configs = new ArrayList<>();
        Object obj = null;

        if (result == null) {
            return configs;
        }

        if (result instanceof Collection) {
            if (((Collection) result).stream().allMatch(r -> r instanceof Assignment)) {
                obj = ((Collection) result).stream().findAny().get();
            }
        }
        else if (result instanceof PageResult) {
            if (((PageResult) result).getRecords().get(0) instanceof Assignment) {
                obj = ((PageResult) result).getRecords().get(0);
            }
        } else if (result instanceof Assignment) {
            obj = result;
        }

        if (null != obj) {
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) { // 遍历所有属性
                field.setAccessible(true);
                if (field.isAnnotationPresent(CodeMappingConfig.class)) {
                    CodeMappingConfig config = field.getAnnotation(CodeMappingConfig.class);
                    configs.add(new MappingConfig(config.fieldCodeProperty(), field.getName(), config.targetClass(), config.targetClassKeyProperty(), config.targetClassFieldProperty(), config.targetClassName()));
                }
            }
        }

        return configs;
    }

    /**
     * 根据类型找出其属性标注的[码-名]映射配置
     * @param rootClass
     * @return 映射配置列表
     */
    private List<MappingConfig> getMappingConfigs(Class rootClass) {
        List<MappingConfig> configs = new LinkedList<>();//new ArrayList<>();//改为链表 保证其按配置顺序执行

        if (rootClass != null) {
            Field[] fields = rootClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if (field.isAnnotationPresent(CodeMappingConfig.class)) {
                    CodeMappingConfig config = field.getAnnotation(CodeMappingConfig.class);
                    configs.add(new MappingConfig(config.fieldCodeProperty(), field.getName(), config.targetClass(), config.targetClassKeyProperty(), config.targetClassFieldProperty(), config.targetClassName()));
                }
            }
            //获得父类的属性配置
            if (rootClass.getSuperclass() != Object.class) {
                configs.addAll(getMappingConfigs(rootClass.getSuperclass()));
            }
        }

        return configs;
    }

    /**
     * 校验映射配置各属性设置
     * @param rootClassInfo
     * @param mappingConfigs
     * @return
     */
    private String checkMappingParams(RootClassInfo rootClassInfo, List<MappingConfig> mappingConfigs) {
        StringBuilder sb = new StringBuilder("");
        if (StringUtils.isBlank(rootClassInfo.getRootClassName()) && rootClassInfo.getRootClass() == Object.class) {
            sb.append("码名映射实体对象类型或类全名必须设置其中一个属性[rootClass,rootClassName]").append("\r\n");
        }
        mappingConfigs.stream().forEach(c -> {
            if (StringUtils.isAnyBlank(c.getFieldCodeProperty(), c.getFieldNameProperty(), c.getTargetClassKeyProperty(), c.getTargetClassFieldProperty())) {
                sb.append("@CodeMappingConfig注解中: fieldCodeProperty,fieldNameProperty,targetClassKeyProperty,targetClassFieldProperty 这四个属性必须都得设置其值").append("\r\n");
                ;
            }
            if (StringUtils.isBlank(c.getTargetClassName()) && c.getTargetClass() == Object.class) {
                sb.append("@CodeMappingConfig注解中: targetClass ,targetClassName 必须设置其中一个属性").append("\r\n");
            }
        });

        return sb.toString();
    }

}